// 注意：该文件由脚本自动生成🎉🎉🎉
/**
 * @Description: 路由缓存工具
 * @Author BPF
 * @create 2021/4/15 9:37
 */
import { uuid, isFunction, eachTree, def, isPlainObject, _typeof, isString } from 'sf-utils2'
import { createRouter } from 'vue-router'
import path from 'path'
import { cloneVNode, defineComponent, unref } from 'vue'
import ScrollView from '../ScrollView'

export function createLazyRouter(
  options = {
    base: '/',
    history: undefined,
    routes: [],
    scrollBehavior: undefined,
    parseQuery: undefined,
    stringifyQuery: undefined,
    linkActiveClass: undefined,
    linkExactActiveClass: undefined
  }
) {
  // console.log('history', history)
  // const base = path.join('/', history.base?.split('#')?.[1] || '/')
  enhanceLoopRoutes(options.routes, options.base)
  // console.log('options.routes', options.routes)
  const innerRouter = createRouter(options)
  let router = {
    __proto__: innerRouter
  }
  def(router, 'base', options.base)
  enhanceProxyRouter(router)
  // console.log('roete', router)
  return router
}

/**
 * 异步加载路由
 * @param component  异步路由
 * @param routerConfig  路由配置对象，缓存路由时用到
 * @returns {Promise<unknown>}
 */
export async function lazy(component = Promise.resolve(), routerConfig = {}) {
  if (!component) return console.error('传入lazy函数component必须有值')
  const isFun = isFunction(component) || _typeof(component) === 'asyncFunction'
  const _component = isFun ? await component() : await component

  const defaultComponentName = `ScrollView_${uuid().replace(/-/g, '').slice(-12)}` // value?.__file?.slice?.(0, -4)?.replaceAll('/', '-')

  // 自定义包裹的组件
  const WrapComp = defineComponent({
    name: defaultComponentName,
    setup(props, { slots }) {
      let defaults = slots.default?.() || []
      return () =>
        cloneVNode(
          <ScrollView showBackTop={false}>
            {defaults.map((vnode) => cloneVNode(vnode, { ...props, class: ['min-h-full'], ref: 'route@ref' }))}
          </ScrollView>,
          { innerScrollViewClass: ['pr-16px'] }
        )
    }
  })

  console.log('_component', _component, routerConfig?.name)

  if (isPlainObject(_component) || _typeof(_component) === 'Module') {
    Object.values(_component).forEach(() => {
      // value
      // if (Object.hasOwn(value, 'render') || Object.hasOwn(value, '__file')) {
      // 是vue组件
      if (routerConfig.meta?.cache !== false) {
        WrapComp.name = routerConfig.name = routerConfig?.name || defaultComponentName
      }
      // }
    })
  }

  return Promise.resolve(
    <WrapComp>
      <_component.default />
    </WrapComp>
  )
  // return Promise.resolve(_component)
}

/**
 * 递归处理 路由, 给其加上路由缓存 name，并且会给每个路由自动加上前缀
 * 出了path等于/，其他都是默认加上前缀
 * @param routes
 * @param base 路径
 * @returns {[]}
 */
export function enhanceLoopRoutes(routes = [], base = '/') {
  if (!routes) return []
  routes = Array.isArray(routes) ? routes : [routes]
  return eachTree({
    tree: routes,
    props: { children: 'children' },
    isDeepClone: false,
    callbackItem: (item, index, list, parentObj) => {
      // item => 当前树节点, index => 当前树节点索引, list => 当前树节点数组, parentObj => 父节点
      // console.log('回调函数的值@callbackItem', item, index, list, parentObj)
      // 如果出现path 等于 /，那么不做处理
      const pathName = item.path?.split?.('?')?.at(0) || item.path
      const matches = pathName.match(/(.*?\/):(\w+)/)
      if (matches?.length) {
        item.exp = new RegExp(`^${matches[1]}`)
        item.__isDynamic = true
      } else {
        item.exp = new RegExp(`^${pathName}$`)
        item.__isDynamic = false
      }
      def(item, 'exp')
      def(item, '__isDynamic')
      if (item.path !== '/') {
        if (!item.path.startsWith(parentObj?.path)) {
          item.path = path.join(parentObj?.path || '/', item.path || '/')
        }
        !item.path.startsWith(base) && (item.path = path.join(base, item.path))
      }
      if (Object.hasOwn(item, 'redirect') && item.redirect !== '/') {
        item.redirect = path.join(base, item.redirect || '/')
      }
      if (
        item.__level__ == 2 &&
        item.meta?.isScroll !== false &&
        item.meta?.cache !== false &&
        Object.hasOwn(item, 'component')
      ) {
        if (!item.component?.isLoaded) {
          let componentRef = item.component // 先引用之前的，形成闭包
          item.name = 'ScrollView_' + uuid().replace(/-/g, '').slice(-12)
          item.component = () => lazy(componentRef, item)
          item.component.isLoaded = true
        }
      }
    }
  })
}

/**
 * 判断是否是 RouteRecordRaw 类型
 * @param routeRecordRaw
 * @returns {boolean}
 */
export function isRouteRecordRaw(routeRecordRaw) {
  return (
    Object.hasOwn(routeRecordRaw, 'component') &&
    routeRecordRaw.component &&
    (Object.hasOwn(routeRecordRaw, 'path') ||
      Object.hasOwn(routeRecordRaw, 'name') ||
      Object.hasOwn(routeRecordRaw, 'meta'))
  )
}

/**
 * 代理router实例对象
 * @router 路由实例
 */
export function enhanceProxyRouter(router = {}) {
  const obj = Object.create(null)
  obj.__proto__ = router
  const routerPrototype = router.__proto__
  const routerMethods = Object.create(routerPrototype)
  router.__proto__ = routerMethods

  def(routerMethods, '__vars', {
    refreshRoute: undefined // 是否刷新路由
    // history: [], // 历史访问过历史
    // routeDirection: '' // 路由方向 forward：前进 backward：后退
  })

  const addRoutesMethodList = [{ methodName: 'addRoutes' }, { methodName: 'addRoute' }]
  addRoutesMethodList.forEach(({ methodName }) => {
    const origin = arrayPrototype.__proto__[methodName]
    const args = [...arguments]
    def(routerMethods, methodName, function () {
      switch (methodName) {
        case 'addRoute': {
          if (args.length === 1) {
            // 只有一个参数
            enhanceLoopRoutes(args[0], router.base)
          } else if (args.length === 2) {
            // 两个参数
            enhanceLoopRoutes(args[1], router.base)
          }
          break
        }
        case 'addRoutes': {
          const [$0] = args
          if (Array.isArray($0)) {
            isRouteRecordRaw($0[0]) && enhanceLoopRoutes($0, router.base)
          }
          break
        }
      }
      return origin.apply(this, args)
    })
  })

  const stateChangeMethodList = [
    { methodName: 'push', routeDirection: 'forward' },
    { methodName: 'back', routeDirection: 'backward' },
    { methodName: 'go', routeDirection: 'backward' },
    { methodName: 'replace', routeDirection: '' }
  ]

  stateChangeMethodList.forEach(({ methodName }) => {
    const origin = routerMethods.__proto__[methodName]
    // const vars = routerMethods.__vars
    const _backRouteRefresh = function () {
      const fn = function () {
        routerMethods.__vars?.refreshRoute?.call?.(this, unref(this.currentRoute)?.fullPath)
        window.removeEventListener(`hashchange`, fnBind, false)
      }
      const fnBind = fn.bind(this)
      window.addEventListener(`hashchange`, fnBind, false)
    }
    def(routerMethods, methodName, function () {
      switch (methodName) {
        case 'replace':
        case 'push': {
          if (isString(arguments[0])) {
            arguments[0] = path.join(router.base, arguments[0])
          } else if (isPlainObject(arguments[0])) {
            arguments[0].path = path.join(router.base, arguments[0]?.path || '/')
            if (arguments[0]?.refresh === true) {
              routerMethods.__vars?.refreshRoute?.call?.(this, ...arguments)
            }
          }
          break
        }
        case 'back': {
          if (isPlainObject(arguments[0])) {
            if (arguments[0]?.refresh === true) {
              _backRouteRefresh.call(this)
            }
          }
          break
        }
        case 'go': {
          if (isPlainObject(arguments[1])) {
            if (arguments[1]?.refresh === true) {
              _backRouteRefresh.call(this)
            }
          }
          break
        }
        // eslint-disable-next-line no-empty
        default: {
        }
      }

      let result = origin.apply(this, arguments)
      // window.location.hash = '/user'
      // if (isPromise(result)) {
      //   result.then(() => {})
      // } else {
      // }
      // window.location.hash = `#/Project/Addsss`
      return result
    })
  })

  // 跳转之前
  router.beforeEach((to, from, next) => {
    // document.title = to.meta.label || 'default'
    next()
  })

  // 跳转之后
  router.afterEach(() => {})
}

/**
 * 生成导航信息
 * @param route
 * @returns {{fullPath: *, path, meta, query, name: *, title, params, key: *}}
 */
export const generateNavAttr = (route = {}) => {
  return {
    name: route.matched?.slice(-1)?.shift()?.components?.default?.name || route.name,
    path: route.path,
    fullPath: route.fullPath || route.path,
    meta: route.meta,
    title: route.title || route?.meta?.title,
    params: route.params,
    query: route.query,
    index: route.fullPath || route.path,
    key: route.key || route.fullPath
  }
}

let arrayPrototype = Array.prototype
let arrayMethods = Object.create(arrayPrototype)
let methodsNeedChange = ['shift', 'unshift', 'pop', 'push', 'splice', 'reversed', 'sort']

/**
 * // 代理数组原型方法
 * @param target
 * @param cb
 */
export const proxyArrayProto = (target = [], cb) => {
  target.__proto__ = arrayMethods
  methodsNeedChange.forEach((methodName) => {
    def(arrayMethods, methodName, function () {
      const origin = arrayPrototype[methodName]
      let args = [...arguments]
      const result = origin.apply(this, args)
      cb && cb(target)
      return result
    })
  })
}

/**
 * 判断是否是vue的组件
 * @param vm
 * @returns {*|boolean}
 */
export function isVueComponent(vm) {
  return vm?.__v_isVNode && isPlainObject(vm?.component?.type)
}

/**
 * 根据fullPath，删除缓存中的路由
 * @param {String} key
 * @param {VueComponent} keepAliveInstance
 */
export function rmCacheRoute(key = '', keepAliveInstance) {
  // 删除组件在keepalive里的cache，参考 pruneCacheEntry 这个方法的逻辑
  // fix: https://github.com/vuejs/vue-next/issues/2077
  // keepAliveInstance ||= keepAliveRef.value.$
  const cache = keepAliveInstance.__v_cache
  const vnode = cache?.get?.(key)
  if (vnode) {
    let shapeFlag = vnode.shapeFlag
    if (shapeFlag & 256 /* COMPONENT_SHOULD_KEEP_ALIVE */) {
      shapeFlag -= 256 /* COMPONENT_SHOULD_KEEP_ALIVE */
    }
    if (shapeFlag & 512 /* COMPONENT_KEPT_ALIVE */) {
      shapeFlag -= 512 /* COMPONENT_KEPT_ALIVE */
    }
    vnode.shapeFlag = shapeFlag
    const renderer = keepAliveInstance.ctx.renderer
    renderer.um(vnode, keepAliveInstance, keepAliveInstance.suspense)
    cache.delete(key)
  }
}

/**
 * vue的生命周期简写映射
 * @type {{rtc: string, bc: string, a: string, c: string, rtg: string, bm: string, m: string, bu: string, u: string, um: string, bum: string, sp: string, da: string, ec: string}}
 */
export const VUE_LIFECYCLE_CONFIG = {
  ['sp' /* SERVER_PREFETCH */]: 'serverPrefetch',
  ['bc' /* BEFORE_CREATE */]: 'beforeCreate',
  ['c' /* CREATED */]: 'created',
  ['bm' /* BEFORE_MOUNT */]: 'beforeMount',
  ['m' /* MOUNTED */]: 'mounted',
  ['bu' /* BEFORE_UPDATE */]: 'beforeUpdate',
  ['u' /* UPDATED */]: 'updated',
  ['bum' /* BEFORE_UNMOUNT */]: 'beforeUnmount',
  ['um' /* UNMOUNTED */]: 'unmounted',
  ['a' /* ACTIVATED */]: 'activated',
  ['da' /* DEACTIVATED */]: 'deactivated',
  ['ec' /* ERROR_CAPTURED */]: 'errorCaptured',
  ['rtc' /* RENDER_TRACKED */]: 'renderTracked',
  ['rtg' /* RENDER_TRIGGERED */]: 'renderTriggered'
}

/**
 * 递归调用子/孙组件的生命周期方法。
 * @param {Array} children 子组件实例
 * @param {String} hook
 */
export function _loopCallChildHook(children = [], hook = '') {
  if (Array.isArray(children)) {
    const hookFullName = VUE_LIFECYCLE_CONFIG[hook]
    children.forEach((vm) => {
      vm.type?.[hookFullName]?.()
      if (isVueComponent(vm)) {
        Array.isArray(vm?.component?.[hook]) && vm.component[hook].forEach((f) => f?.())
        Array.isArray(vm?.$?.[hook]) && vm.$[hook].forEach((f) => f?.())
      }
      _loopCallChildHook(vm?.children, hook)
    })
  }
}

/**
 * 激活子组件中所有的 activated 事件
 * @param vm
 */
export function activateChildComponent(vm) {
  const children = [vm.$, vm.$?.subTree?.children].flat(Infinity).filter(Boolean)
  _loopCallChildHook(children, 'a') // activated
}

/**
 * 激活子组件中所有的 deactivated 事件
 * @param vm
 */
export function deactivateChildComponent(vm) {
  const children = [vm.$, vm.$?.subTree?.children].flat(Infinity).filter(Boolean)
  _loopCallChildHook(children, 'da') // activated
}
